#include "Iso7816Plugin.h"


Iso7816Plugin::Iso7816Plugin() : Plugin("iso7816")
{
}

void Iso7816Plugin::InitializePlugin( PluginSpMapSp plugins, INameResolutorSp nameResolutor )
{
	Plugin::InitializePlugin(plugins, nameResolutor);
}

void Iso7816Plugin::ExecuteCommand(CommandSp cmd)
{
	Plugin::ExecuteCommand(cmd);

	vars = SValueSpMapSp(new SValueSpMap());
	(*vars)["connection"] = inputs->Get<SNumber>("connection");

	if ( commandName == "Transmit0")
	{
		(*vars)["protocol"] = SEnum::Create( descriptorsRepository->GetEnumTypeDescriptor("pcsc.Protocol"), "SCARD_PROTOCOL_T0");
	}
	else
	{
		(*vars)["protocol"] = inputs->Get<SEnum>("protocol");
	}

	// Call the right command handler
	if ( commandName == "GetResponse" )	Iso7816_GetResponse();
	else if ( commandName == "ReadBinary" )	Iso7816_ReadBinary();
	else if ( commandName == "Select" )	Iso7816_Select();
	else if ( commandName == "Transmit0" )	Iso7816_Transmit0();
	else if ( commandName == "UpdateBinary" )	Iso7816_UpdateBinary();
	else if ( commandName == "Verify" )	Iso7816_Verify();
	else throw SPluginException("Error: command not implemented by plugin.");
}

void Iso7816Plugin::Iso7816_GetResponse()
{
	// Insert plugin operations here
	string input = Utility::Format("0x{0,nx}C00000{1,nx}", "CLA length", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	SVectorSp data = SVector::Create( descriptorsRepository->GetVectorTypeDescriptor("pcsc.Buffer") );

	int len = receiveBuffer->GetLength();
	if ( len > 2)
	{
		for(int i=0; i<(int)receiveBuffer->GetLength()-2; ++i)
		{
			data->AddItem( receiveBuffer->Get(i) );
		}
	}

	// Setting outputs
	outputs->Set("data", data);

	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(len-2)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(len-1)->Get();
	SetCommonOutputs(sw1, sw2);

	string desc = "No info available.";
	if ( sw1 == 0x61 ) desc = "Normal processing: more data bytes are available (indicated by sw2)";
	if ( sw1 == 0x62 && sw2 == 0x81) desc = "Part of returned data may be corrupted";
	if ( sw1 == 0x67 && sw2 == 0x00) desc = "Wrong length.";
	if ( sw1 == 0x6A && sw2 == 0x86) desc = "Incorrect parameters P1-P2";
	if ( sw1 == 0x90 && sw2 == 0x00) desc = "Normal ending of command";
	if ( sw1 == 0x6C) desc = "Wrong length";

	outputs->Set("desc", SString::Create(desc));
}

void Iso7816Plugin::Iso7816_ReadBinary()
{
	// Input preparation   
	SNumberSp offset  = inputs->Get<SNumber>("offset");
	SByteSp length  = inputs->Get<SByte>("length");
	SByteSp cla = inputs->Get<SByte>("CLA");

	BYTE offsetMSB = offset->Get() / 256;
	BYTE offsetLSB = offset->Get() % 256;
	(*vars)["offsetMSB"] = SByte::Create(offsetMSB);
	(*vars)["offsetLSB"] = SByte::Create(offsetLSB);
	(*vars)["length"] = length;
	(*vars)["CLA"] = cla;

	// Insert plugin operations here
	string input = Utility::Format("0x{0,nx}B0{1,nx}{2,nx}{3,nx}", "CLA offsetMSB offsetLSB length", vars);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	SVectorSp data = SVector::Create( descriptorsRepository->GetVectorTypeDescriptor("pcsc.Buffer") );

	int len = receiveBuffer->GetLength();
	if ( len > 2)
	{
		for(int i=0; i<(int)receiveBuffer->GetLength()-2; ++i)
		{
			data->AddItem( receiveBuffer->Get(i) );
		}
	}

	outputs->Set("data", data);

	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(len-2)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(len-1)->Get();
	SetCommonOutputs(sw1, sw2);

	string desc = "No info available.";
	if ( sw1 == 0x62 && sw2 == 0x81) desc = "part of returned data may be corrupted";
	if ( sw1 == 0x62 && sw2 == 0x82) desc = "End of file reached before reading the length";
	if ( sw1 == 0x67 && sw2 == 0x00) desc = "Wrong length";
	if ( sw1 == 0x69 && sw2 == 0x81) desc = "Command incompatible with file structure";
	if ( sw1 == 0x69 && sw2 == 0x82) desc = "Security status not satisfied";
	if ( sw1 == 0x69 && sw2 == 0x86) desc = "Command not allowed (no current EF)";
	if ( sw1 == 0x6A && sw2 == 0x81) desc = "Function not supported";
	if ( sw1 == 0x6A && sw2 == 0x82) desc = "File not found";
	if ( sw1 == 0x6B && sw2 == 0x00) desc = "Wrong parameters (offset outside the EF)";
	if ( sw1 == 0x90 && sw2 == 0x00) desc = "Normal ending of command";
	if ( sw1 == 0x6C) desc = "Wrong length";

	outputs->Set("desc", SString::Create(desc));
}

void Iso7816Plugin::Iso7816_Select()
{
	// Insert plugin operations here
	string input = Utility::Format("0x{0,nx}A4000002{1,4nx}", "CLA id", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);

	string desc = "No info available.";
	if ( sw1 == 0x61 ) desc = "sw2 bytes of the response data";
	if ( sw1 == 0x62 && sw2 == 0x83) desc = "Selected file invalidated";
	if ( sw1 == 0x62 && sw2 == 0x84) desc = "FCI not well formatted";
	if ( sw1 == 0x6A && sw2 == 0x81) desc = "Function not supported";
	if ( sw1 == 0x6A && sw2 == 0x82) desc = "File not found";
	if ( sw1 == 0x6A && sw2 == 0x86) desc = "Incorrect parameters P1-P2";
	if ( sw1 == 0x90 && sw2 == 0x00) desc = "Normal ending of command";

	outputs->Set("desc", SString::Create(desc));
}

void Iso7816Plugin::Iso7816_Transmit0()
{
	// Insert plugin operations here
	string input = Utility::Format("0x{0,nx}{1,nx}{2,nx}{3,nx}{4,nx}{5,nx}", "CLA INS P1 P2 P3 data", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);
}

void Iso7816Plugin::Iso7816_UpdateBinary()
{
	// Input preparation   
	SNumberSp offset  = inputs->Get<SNumber>("offset");
	SByteSp length  = inputs->Get<SByte>("length");
	SVectorSp data = inputs->Get<SVector>("data");
	SByteSp cla = inputs->Get<SByte>("CLA");

	BYTE offsetMSB = offset->Get() / 256;
	BYTE offsetLSB = offset->Get() % 256;
	(*vars)["offsetMSB"] = SByte::Create(offsetMSB);
	(*vars)["offsetLSB"] = SByte::Create(offsetLSB);
	(*vars)["length"] = length;
	(*vars)["data"] = data;
	(*vars)["CLA"] = cla;

	// Insert plugin operations here
	string input = Utility::Format("0x{0,nx}D6{1,nx}{2,nx}{3,nx}{4,nx}", "CLA offsetMSB offsetLSB length data", vars);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);

	string desc = "No info available.";
	if ( sw1 == 0x65 && sw2 == 0x81) desc = "Memory failure (unsuccessful writing)";
	if ( sw1 == 0x67 && sw2 == 0x00) desc = "Wrong length";
	if ( sw1 == 0x69 && sw2 == 0x81) desc = "Command incompatible with file structure";
	if ( sw1 == 0x69 && sw2 == 0x82) desc = "Security status not satisfied";
	if ( sw1 == 0x69 && sw2 == 0x86) desc = "Command not allowed";
	if ( sw1 == 0x6A && sw2 == 0x81) desc = "Function not supported";
	if ( sw1 == 0x6A && sw2 == 0x82) desc = "File not found";
	if ( sw1 == 0x6B && sw2 == 0x00) desc = "Wrong parameters";
	if ( sw1 == 0x90 && sw2 == 0x00) desc = "Normal ending of command";

	outputs->Set("desc", SString::Create(desc));
}

void Iso7816Plugin::Iso7816_Verify()
{
	// Insert plugin operations here
	string input = Utility::Format("0x{0,nx}2000{1,nx}{2,nx}{3,8nx}", "CLA chvType length chvKey", inputs);
	SVectorSp receiveBuffer = ExecutePcscTransmit(input);

	// Setting outputs
	BYTE sw1 = (BYTE) receiveBuffer->Get<SByte>(0)->Get();
	BYTE sw2 = (BYTE) receiveBuffer->Get<SByte>(1)->Get();
	SetCommonOutputs(sw1, sw2);

	string desc = "No info available.";
	if ( sw1 == 0x63) desc = "Verification failed.";
	if ( sw1 == 0x69 && sw2 == 0x83) desc = "Authentication method blocked";
	if ( sw1 == 0x69 && sw2 == 0x84) desc = "Referenced data invalidated";
	if ( sw1 == 0x6A && sw2 == 0x86) desc = "Incorrect parameters P1-P2";
	if ( sw1 == 0x6A && sw2 == 0x88) desc = "Referenced data not found";
	if ( sw1 == 0x90 && sw2 == 0x00) desc = "Normal ending of command";

	outputs->Set("desc", SString::Create(desc));
}


/*****************************************************/
/*               Usefull methods                     */
/*****************************************************/

SVectorSp Iso7816Plugin::ExecutePcscTransmit( string input )
{
	CommandDescriptorSp transmitCd = descriptorsRepository->GetCommandDescriptor("pcsc.Transmit");
	CommandSp transmitCmd = Command::Create(transmitCd, input, vars);
	(*plugins)["pcsc"]->ExecuteCommand(transmitCmd);

	SStructSp transmitOutputs = transmitCmd->GetOutputs();

	SEnumSp res = transmitOutputs->Get<SEnum>("res");
	SVectorSp receiveBuffer = transmitOutputs->Get<SVector>("receiveBuffer");

	if ( res->Get() != "SCARD_S_SUCCESS")
	{
		throw SPluginException("There is not a valid pcsc connection extablished or there is an error in the structure of the command sent.");
	}

	return receiveBuffer;
}

void Iso7816Plugin::SetCommonOutputs( BYTE sw1, BYTE sw2 )
{
	outputs->Set("sw1", SByte::Create(sw1) );
	outputs->Set("sw2", SByte::Create(sw2) );
}

